#include "cybertron/node/StartupArguments.hpp"
#include "cybertron/core/JsonReader.hpp"
#include "cybertron/core/UtilFile.hpp"
#include "cybertron/core/Log.hpp"
//#include "Node/Coordinator.pb.h"
#include "cybertron/core/ConfigureClient.hpp"
#include "cybertron/core/UtilPath.hpp"
#include "cybertron/core/SimOneEnv.hpp"

CYBERTRON_BEGIN

CybertronCoreAPI std::string StartupArguments::ImageUuidNodeTimer()
{
	return "CybertronNodeTimer";
}

CybertronCoreAPI StartupArguments& theStartupArguments()
{
	static StartupArguments sArguments;
	return sArguments;
}

CybertronCoreAPI StartupArguments::StartupArguments()
{
	zeroMembers();
}

CybertronCoreAPI StartupArguments::StartupArguments(const StartupArguments& rhs)
{
	mWorldConfigJson = rhs.mWorldConfigJson;
	mNodeConfigJson = rhs.mNodeConfigJson;
	mNetwork = rhs.mNetwork;
	mThisNodeInstanceId = rhs.mThisNodeInstanceId;
	mThisNodePort = rhs.mThisNodePort;
}

CybertronCoreAPI StartupArguments& StartupArguments::operator = (const StartupArguments& rhs)
{
	mWorldConfigJson = rhs.mWorldConfigJson;
	mNodeConfigJson = rhs.mNodeConfigJson;
	mNetwork = rhs.mNetwork;
	mThisNodeInstanceId = rhs.mThisNodeInstanceId;
	mThisNodePort = rhs.mThisNodePort;

	return *this;
}

CybertronCoreAPI StartupArguments::~StartupArguments()
{
	clear();
}

void StartupArguments::zeroMembers()
{
	mThisNodeInstanceId = -1;
	mThisNodePort = 0;
	mCCCMode = false;

    mArgc = 0;
    mArgv = nullptr;
}

CybertronCoreAPI bool StartupArguments::parse(int argc, char* argv[], const std::string& appSingleParam, std::string& value)
{
	std::map<std::string, std::string> arguments;
	arguments[appSingleParam] = value;
	bool succeed = parse(argc, argv, arguments);
	if (succeed)
	{
		value = arguments[appSingleParam];
		return true;
	}

	return false;
}

CybertronCoreAPI bool StartupArguments::parse(int argc, char* argv[], std::map<std::string, std::string>& arguments)
{
	for (int index = 1; index < argc; index++)
	{
		std::map<std::string, std::string>::iterator it = arguments.find(std::string(argv[index]));
		if (it != arguments.end())
		{
			if (index + 1 < argc)
			{
				it->second = std::string(argv[index + 1]);
			}
			index++;
		}
	}

	return parse(argc, argv);
}

CybertronCoreAPI bool StartupArguments::parse(int argc, char* argv[])
{
	clear();

    mArgc = argc;
    mArgv = argv;

	for (int i = 1; i < argc; ++i)
	{
		std::string arg = argv[i];
		if (arg == "--appname")
		{
			++i;
			if (i < argc)
			{
				if (!setAppName(argv[i]))
				{
					return false;
				}
			}
		}
		else if (arg == "--appid")
		{
			++i;
			if (i < argc)
			{
				if (!setAppId(argv[i]))
				{
					return false;
				}
			}
		}
		//else if (arg == "--node")
		//{
		//	++i;
		//	if (i < argc)
		//	{
		//		if (!setConfigFilenameNode(argv[i]))
		//		{
		//			return false;
		//		}
		//	}
		//}
		//else if (arg == "--network")
		//{
		//	++i;
		//	if (i < argc)
		//	{
		//		if (!setConfigFilenameNetwork(argv[i]))
		//		{
		//			return false;
		//		}
		//	}
		//}
		//else if (arg == "--port")
		//{
		//	++i;
		//	if (i < argc)
		//	{
		//		if (!setListeningPort(argv[i]))
		//		{
		//			return false;
		//		}
		//	}
		//}
		//else if (arg == "--instance")
		//{
		//	++i;
		//	if (i < argc)
		//	{
		//		if (!setInstanceId(argv[i]))
		//		{
		//			return false;
		//		}
		//	}
		//}
		//else if (arg == "--logip")
		//{
		//	++i;
		//	if (i < argc)
		//	{
		//		if (!setLogIp(argv[i]))
		//		{
		//			return false;
		//		}
		//	}
		//}
		//else if (arg == "--logport")
		//{
		//	++i;
		//	if (i < argc)
		//	{
		//		if (!setLogPort(argv[i]))
		//		{
		//			return false;
		//		}
		//	}
		//}
	}

	return true;
}

CybertronCoreAPI void StartupArguments::clear()
{
	mWorldConfigJson.clear();
	mNodeConfigJson.clear();
	mNetwork.clear();

	zeroMembers();
}

//CybertronCoreAPI void StartupArguments::toPassStart(Coordinator::FromAnyRequestTaskStart& taskStart) const
//{
//	taskStart.set_worldconfigjson(mWorldConfigJson);
//	Coordinator::NodeWork* pWork = taskStart.mutable_work();
//	pWork->set_imageconfigjson(mNodeConfigJson);
//	pWork->set_instanceid(mThisNodeInstanceId);
//	pWork->set_port(mThisNodePort);
//
//	for (size_t i = 0; i < mNetwork.size(); ++i)
//	{
//		Coordinator::NodeDescription* pNode = taskStart.add_nodes();
//		pNode->set_imageuuid(mNetwork[i].imageUuid);
//		pNode->set_instanceid(mNetwork[i].instanceId);
//		pNode->set_ip(mNetwork[i].ip);
//		pNode->set_port(mNetwork[i].port);
//
//		if (mNetwork[i].instanceId == mThisNodeInstanceId)
//		{
//			pWork->set_imageuuid(pWork->imageuuid());
//		}
//	}
//}

CybertronCoreAPI const std::string& StartupArguments::getConfigStringWorld() const
{
	return mWorldConfigJson;
}

CybertronCoreAPI const std::string& StartupArguments::getConfigStringNode() const
{
	return mNodeConfigJson;
}

CybertronCoreAPI const std::vector<StartupArguments::NodeDescription>&
StartupArguments::getConfigNetwork() const
{
	return mNetwork;
}

CybertronCoreAPI int StartupArguments::getThisNodeInstanceId() const
{
	return mThisNodeInstanceId;
}

CybertronCoreAPI int StartupArguments::getThisNodePort() const
{
	return mThisNodePort;
}

CybertronCoreAPI const StartupArguments::NodeDescription*
StartupArguments::findNodeByImage(const std::string& imageUuid, size_t offset) const
{
	for (size_t i = offset; i < mNetwork.size(); ++i)
	{
		if (mNetwork[i].imageUuid == imageUuid)
		{
			return &mNetwork[i];
		}
	}

	return nullptr;
}

CybertronCoreAPI const StartupArguments::NodeDescription*
StartupArguments::findNodeTimer(size_t offset)
{
	return findNodeByImage(ImageUuidNodeTimer(), offset);
}
CybertronCoreAPI bool StartupArguments::setAppName(const std::string& name) {
	mAppName = name;
	return true;
}
CybertronCoreAPI bool StartupArguments::setAppId(const std::string& id){
	mAppId = id;
	return true;
}

CybertronCoreAPI std::string StartupArguments::getAppName() {
	return mAppName;
}

CybertronCoreAPI std::string StartupArguments::getAppId() {
	return mAppId;
}
CybertronCoreAPI bool StartupArguments::setConfigFilenameWorld(const std::string& file)
{
	if (!UtilFile::exist(file))
	{
		logError("World config file not found: %s", file.c_str());
		return false;
	}
	mWorldConfigJson = UtilFile::readUTF8StringFromFile(file);
	logSilent("World config: %s", mWorldConfigJson.c_str());
	return true;
}

CybertronCoreAPI bool StartupArguments::setConfigStringWorld(const std::string & str)
{
	mWorldConfigJson = str;
	logSilent("World config: %s", mWorldConfigJson.c_str());
	return true;
}

CybertronCoreAPI bool StartupArguments::setConfigFilenameNode(const std::string& file)
{
	if (!UtilFile::exist(file))
	{
		logError("Node config file not found: %s", file.c_str());
		return false;
	}
	mNodeConfigJson = UtilFile::readUTF8StringFromFile(file);
	logSilent("Node config: %s", mNodeConfigJson.c_str());
	return true;
}

CybertronCoreAPI bool StartupArguments::setConfigStringNode(const std::string & str)
{
	mNodeConfigJson = str;
	logSilent("Node config: %s", mNodeConfigJson.c_str());
	return true;
}

CybertronCoreAPI bool StartupArguments::setConfigFilenameNetwork(const std::string& file)
{
	if (!UtilFile::exist(file))
	{
		logError("Network config file not found: %s", file.c_str());
		return false;
	}
	return setConfigStringNetwork(UtilFile::readUTF8StringFromFile(file));
}

CybertronCoreAPI bool StartupArguments::setConfigStringNetwork(const std::string & str)
{
	logSilent("Network config: %s", str.c_str());
	nlohmann::json jsonNetwork;
	if (!JsonReader::loadString(jsonNetwork, str))
	{
		logError("Network config format failed: %s", str.c_str());
		return false;
	}
	return setConfigNetwork(jsonNetwork);
}

CybertronCoreAPI bool StartupArguments::setConfigNetwork(nlohmann::json& json)
{
	if (!json.is_array())
	{
		return false;
	}
	for (size_t i = 0; i < json.size(); ++i)
	{
		const auto& jsonNode = json[i];
		NodeDescription node;
		node.ip = JsonReader::getString(jsonNode, "ip");
		node.imageUuid = JsonReader::getString(jsonNode, "imageUuid");
		node.port = JsonReader::getInt(jsonNode, "port");
		node.instanceId = JsonReader::getInt(jsonNode, "instanceId");
		mNetwork.push_back(node);
	}
	return true;
}

CybertronCoreAPI bool StartupArguments::setLogPort(const std::string& logPort)
{
	try
	{
		mLogPort = std::atoi(logPort.c_str());
	}
	catch (std::exception&)
	{
		return false;
	}

	return true;
}

CybertronCoreAPI bool StartupArguments::setLogPort(const int& logPort)
{
	mLogPort = logPort;
	return true;
}

CybertronCoreAPI bool StartupArguments::setLogIp(const std::string& logIp)
{
	mLogIp = logIp;
	return true;
}

CybertronCoreAPI bool StartupArguments::setListeningPort(const std::string& port)
{
	try
	{
		mThisNodePort = std::atoi(port.c_str());
	}
	catch (std::exception&)
	{
		return false;
	}

	return true;
}

CybertronCoreAPI bool StartupArguments::setListeningPort(const int & port)
{
	mThisNodePort = port;
	return true;
}

CybertronCoreAPI bool StartupArguments::setInstanceId(const std::string& instanceId)
{
	try
	{
		mThisNodeInstanceId = std::atoi(instanceId.c_str());
	}
	catch (std::exception&)
	{
		return false;
	}

	return true;
}

CybertronCoreAPI bool StartupArguments::setInstanceId(const int & instanceId)
{
	mThisNodeInstanceId = instanceId;
	return true;
}

CybertronCoreAPI std::string StartupArguments::getLogIp() const
{
	return this->mLogIp;
}
CybertronCoreAPI int StartupArguments::getLogPort() const
{
	return this->mLogPort;
}

CybertronCoreAPI char* StartupArguments::getParamValue(const std::string& param)
{
	if (mCCCMode == false)
	{
		return getenv(param.c_str());
	}
	else
	{
		if (mParamValueMap.find(param) != mParamValueMap.end())
		{
			return (char*)mParamValueMap[param].c_str();
		}

		return getenv(param.c_str());
	}
}

CybertronCoreAPI bool StartupArguments::isCCCMode()
{
	return mCCCMode;
}

CYBERTRON_END
